package socket.socketByNetty.service;


import com.alibaba.fastjson.JSON;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleStateEvent;
import socket.socketByNetty.utils.message.Message;
import socket.socketByNetty.utils.message.MsgType;

/**
 * Created by think on 2017/10/30.
 */
public class EchoServerHandler extends SimpleChannelInboundHandler<String> {

    @Override
    protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
        Message message = JSON.parseObject(msg + "", Message.class);
        System.out.println("接收到消息：" + msg);
        String clientId = message.getClientId();
        if (MsgType.LOGIN.equals(message.getType())) {
            System.out.printf("将%s添加到队列\n", clientId);
            NettyChannelMap.add(clientId, (SocketChannel) ctx.channel());
        } else {
            if (NettyChannelMap.get(clientId) == null) {
                System.out.printf("未登录，请登录!", clientId);
                //说明未登录，或者连接断了，服务器向客户端发起登录请求，让客户端重新登录
                message = new Message(MsgType.LOGIN);
                ctx.channel().writeAndFlush(JSON.toJSONString(message));
            }
        }
        switch (message.getType()) {
            case PING: {
                message = new Message(MsgType.PING);
                NettyChannelMap.get(clientId).writeAndFlush(JSON.toJSONString(message));
            }
            break;
            case SEND: {
                //收到客户端的请求，发送给目标ClientId
                System.out.println("发送消息：" + message);
                if (NettyChannelMap.get(message.getTargetId()) != null) {
                    NettyChannelMap.get(message.getTargetId()).writeAndFlush(JSON.toJSONString(message));
                } else {
                    message.setType(MsgType.NO_TARGET);
                    NettyChannelMap.get(clientId).writeAndFlush(JSON.toJSONString(message));
                }
            }
            break;
            default:
                break;
        }
    }

    /**
     * 心跳和重连机制
     * 为了减轻服务端对几十万心跳包的回复压力，服务端应该"不回复"心跳，采用服务端读数据的方式来确认客户端是否active,（服务端20s读不到数据就是认为客户端挂了）
     * 如果20秒内收不到，服务端可以认为客户端挂了，可以close链路
     * 时间设置在IdleStateHandler(readerIdleTimeSeconds,writerIdleTimeSeconds,allIdleTimeSeconds)
     * 该类的第一个参数是指定读操作空闲秒数，第二个参数是指定写操作的空闲秒数，第三个参数是指定读写空闲秒数
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) evt;
            switch (e.state()) {
                case READER_IDLE:
                    System.out.println("读超时...");
                    ctx.close();
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel active：有新客户端接入");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //channel失效，从Map中移除
        String clientId = NettyChannelMap.remove((SocketChannel) ctx.channel());
        System.out.printf("channel Inactive：%s客户端退出\n", clientId);
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //cause.printStackTrace();
        System.out.println("服务端出现异常！");
    }

     /*@Override
    //如果实现channelRead方法，那messageReceived方法就不会执行
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("有消息来了："+(String)msg);
    }*/

}



